﻿#region Header

/*
    This file is part of ndoctor-statistics.

    ndoctor-statistics is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ndoctor-statistics is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ndoctor-statistics.  If not, see <http://www.gnu.org/licenses/>.
*/

#endregion Header

namespace Probel.NDoctor.Statistics.Components
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    internal class ChartQuery : Query, IChartQuery
    {
        #region Constructors

        public ChartQuery(IRepository repository)
            : base(repository)
        {
        }

        #endregion Constructors

        #region Methods

        /// <summary>
        /// Gets the average execution time by component.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Tuple<string, double>> GetAvgExecutionTimeByComponent()
        {
            var points = (from a in this.Repository.Statistics
                          group a by a.TargetTypeName into g
                          select new Tuple<string, double>(g.Key, g.Average(e => e.ExecutionTime) / 1000))
                          .ToList();
            return new List<Tuple<string, double>>(points.OrderBy(e => e.Item2));
        }

        /// <summary>
        /// Gets the average execution time by method.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Tuple<string, double>> GetAvgExecutionTimeByMethod()
        {
            var points = (from a in this.Repository.Statistics
                          group a by string.Format("{0}.{1}", a.TargetTypeName, a.MethodName) into g
                          select new Tuple<string, double>(g.Key, g.Average(e => e.ExecutionTime) / 1000))
                          .ToList();
            return new List<Tuple<string, double>>(points.OrderBy(e => e.Item2));
        }

        /// <summary>
        /// Gets the bottlenecks by methods.
        /// </summary>
        /// <returns>A chart to be dicplayed</returns>
        public IEnumerable<Tuple<string, double>> GetBottlenecks()
        {
            var points = (from a in this.Repository.Statistics
                          where a.IsPossibleBottleneck == true
                          group a by string.Format("{0}.{1}", a.TargetTypeName, a.MethodName) into g
                          select new Tuple<string, double>(g.Key, g.Count()))
                          .ToList();
            return new List<Tuple<string, double>>(points.OrderBy(e => e.Item2));
        }

        /// <summary>
        /// Get a graph of the average execution time of the methods by day
        /// </summary>
        /// <returns>A chart to be dicplayed</returns>
        public IEnumerable<Tuple<DateTime, double>> GetExecutionTimeGraph()
        {
            var points = (from a in this.Repository.Statistics
                          group a by a.TimeStamp.Date into g
                          select new Tuple<DateTime, double>(g.Key, g.Average(e => e.ExecutionTime)))
                          .ToList();
            return new List<Tuple<DateTime, double>>(points.OrderBy(e => e.Item1));
        }

        /// <summary>
        /// Gets the installation count by date.
        /// </summary>
        /// <returns>
        /// A chart with the installation count by dates
        /// </returns>
        public IEnumerable<Tuple<DateTime, long>> GetInstallationByDates()
        {
            return (from i in this.Repository.Applications
                    group i by i.InstallationDate into g
                    select new Tuple<DateTime, long>(g.Key, g.Count()))
                        .ToArray();
        }

        /// <summary>
        /// Gets the chart for session durations.
        /// </summary>
        /// <returns>
        /// A chart with the session durations
        /// </returns>
        public IEnumerable<Tuple<DateTime, double>> GetSessionDurations()
        {
            return (from s in this.Repository.Durations
                    group s by s.TimeStamp into g
                    select new Tuple<DateTime, double>(g.Key, g.Average(e => e.Duration.TotalMinutes)))
                        .OrderBy(e => e.Item1)
                        .ToArray();
        }

        /// <summary>
        /// Gets the update count by date.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Tuple<DateTime, long>> GetUpdateByDates()
        {
            return (from i in this.Repository.Applications
                    group i by i.UpdateVersion into g
                    select new Tuple<DateTime, long>(g.Key, g.Count()))
                        .ToArray();
        }

        /// <summary>
        /// Gets the date of the usages the by component.
        /// </summary>
        /// <returns>
        /// A chart containing the data to be displayed in a chart
        /// </returns>
        public IEnumerable<Tuple<string, double>> GetUsageByComponent()
        {
            var points = (from a in this.Repository.Statistics
                          group a by a.TargetTypeName into g
                          select new Tuple<string, double>(g.Key, g.Count()))
                          .ToList();
            return new List<Tuple<string, double>>(points.OrderBy(e => e.Item2));
        }

        /// <summary>
        /// Gets the number of times the methods have been called.
        /// </summary>
        /// <returns>A chart to be dicplayed</returns>
        public IEnumerable<Tuple<string, double>> GetUsageByMethods()
        {
            var points = (from a in this.Repository.Statistics
                          group a by string.Format("{0}.{1}", a.TargetTypeName, a.MethodName) into g
                          select new Tuple<string, double>(g.Key, g.Count()));
            return new List<Tuple<string, double>>(points.OrderBy(e => e.Item2))
                          .ToList();
        }

        /// <summary>
        /// Gets the version distribution of nDoctor.
        /// </summary>
        /// <returns>
        /// A graph showing what version is used and its percentage
        /// </returns>
        public IEnumerable<Tuple<string, double>> GetVersionDistribution()
        {
            return (from u in this.Repository.Applications
                    where u.LastUpdate >= DateTime.Today.ToUniversalTime().AddDays(-14)
                    group u by u.Version into g
                    select new Tuple<string, double>(g.Key, g.Count()))
                        .OrderBy(e => e.Item2)
                        .ToArray();
        }

        /// <summary>
        /// Gets the life time by version in minutes.
        /// </summary>
        /// <returns>
        /// A graph to display the total minutes a version has lived
        /// </returns>
        public IEnumerable<Tuple<string, double>> GetVersionLifeTime()
        {
            return (from i in this.Repository.Durations
                    group i by i.Version into g
                    select new Tuple<string, double>(g.Key, g.Sum(e => e.Duration.TotalMinutes)))
                        .ToArray();
        }

        private int GetMethodCount(string targetTypeName, string methodName)
        {
            return (from s in this.Repository.Statistics
                    where s.MethodName == methodName
                       && s.TargetTypeName == targetTypeName
                    select s).Count();
        }

        #endregion Methods
    }
}